import { forEachValue, isPromise } from './utils'
import { reactive, watch,computed } from 'vue'
import { storeKey } from './injectKey'
import ModuleCollection from './module/module-collection'

function getNestedState(state, path) { // 根据路径 获取store.上面的最新状态
    return path.reduce((state, key) => state[key], state)
}

// 后续我们会将store.state 用reactive包裹
function installModule(store, rootState, path, module) { // 递归安装
    // debugger
    let isRoot = !path.length; // 如果数组是空数组 说明是根，否则不是
    
    // 判断是否开启命名空间，构建父子名称 a/c
    const namespaced = store._modules.getNamespaced(path); // [a,c]
    console.log(namespaced,'namespaced');
    // 用户传入的vuex state数据可能有多个嵌套模块，将所有模块的数据整理到一起state中
    if (!isRoot) { // []
        // 收集state，构建state的父子结构数据
        let parentState = path.slice(0, -1).reduce((state, key) => state[key], rootState);
        // 这里为什么要加一个_withCommit呢？因为有动态添加模块的时候，会有动态赋值state操作
        store._withCommit(() => {
            parentState[path[path.length - 1]] = module.state;
        })
    }

    // 统一收集getters、mutation、actions


    // 收集getters  module._raw.getters
    module.forEachGetter((getter, key) => { // {double:function(state){}}
        store._wrappedGetters[namespaced + key] = () => {
            return getter(getNestedState(store.state, path)); // 如果直接使用模块上自己的状态，此状态不是响应式的
        }
    })

  
    // 收集mutation   {add:[mutation]}
    module.forEachMutation((mutation, key) => {
        const entry = store._mutations[namespaced + key] || (store._mutations[namespaced + key] = [])
        entry.push((payload) => { // store.commit('add',payload)
            mutation.call(store, getNestedState(store.state, path), payload)
        })
    })

    
    // 收集actions    mutation和action的一个区别， action执行后返回一个是promise 
    module.forEachAction((action, key) => {
        const entry = store._actions[namespaced + key] || (store._actions[namespaced + key] = []);
        // 因为action 是一个promise，如果用户传入的action不是一个promsie，那就将其包装成promise
        entry.push((payload) => { 
            let res = action.call(store, store, payload);
            // res 是不是一个promise
            if (!isPromise(res)) {
                return Promise.resolve(res);
            }
            return res;
        })
    })

    module.forEachChild((child, key) => { // aCount,bCount
        installModule(store, rootState, path.concat(key), child);
    })
}

// 将state挂载到_state并且实现响应式、收集getters、判断是否开启严格模式
function resetStoreState(store, state) {
    // 将state用reactive变为响应式，存储到data中，方便以后修改
    store._state = reactive({ data: state }); // store._state.data = 'xxcx'

    // 将_wrappedGetters中存放的getters函数变为值，放到state.getters对象中，利用defineProperty
    const wrappedGetters = store._wrappedGetters
    store.getters = {};
    forEachValue(wrappedGetters, (getter, key) => {
        Object.defineProperty(store.getters, key, {
            // 添加计算属性的缓冲
            // get: () => computed(() =>  getter()).value,
            
            // 在vuex中不能用computed实现，因为如果组件销毁了会移除计算属性
            get: getter,
            enumerable: true
        })
    })

    // 如果用户开启严格模式
    if (store.strict) {
        enableStrictMode(store);
    }
}
// 严格模式修改state的数据必须通过 mutions，并且是同步的
function enableStrictMode(store) {
    // 监控数据变变化，数据变化后执行回调函数  effect
    watch(() => store._state.data, () => { 
        console.log(1,store._commiting);
        // vuex的严格模式只会在控制台提示报错，并不会阻止你修改，所以在watch中检测state的变化
        // 如果 _commiting 是false，就会在控制台打印后面的信息。
        // 如果断言为false，则将一个错误消息写入控制台。如果断言是 true，没有任何反应。
        console.assert(store._commiting, 'do not mutate vuex store state outside mutation handlers')
    }, { deep: true, flush: 'sync' }); // deep：深层监控。flush：默认watchApi是异步的，这里改成同步的监控
}
export default class Store {
    // 切片，利用_commiting来监控mutions是否是同步代码
    _withCommit(fn) { 
        // 默认为false
        const commiting = this._commiting;
        this._commiting = true;
        fn();
        this._commiting = commiting
    }
    constructor(options) {
        // ---------------------------------------------------------模块收集
        const store = this;
        store._modules = new ModuleCollection(options);
        




        // ---------------------------------------------------------严格模式 
        // 接受用户是否开启严格模式
        this.strict = options.strict || false; 
        // 在mutation之前添加一个状态 _commiting = true;
        // 调用mutation -> 会更改状态 ， 我就监控这个状态，如果当前状态变化的时候_commiting = true, 同步更改
        this._commiting = false;// 默认为fasle

       



        // ----------------------------------------------------------状态收集
        // 收集getter
        store._wrappedGetters = Object.create(null);
        // 收集 mutation
        store._mutations = Object.create(null);
        // 收集 actions
        store._actions = Object.create(null);
        // 定义状态
        const state = store._modules.root.state; // 根状态
         
        // 构建 getters、mutions、actions的命名空间关系
        // debugger
        installModule(store, state, [], store._modules.root);
         
        // console.log(store,'store');
        // 实现state的响应式，并且将getters函数变为值，放到state.getters对象中
        resetStoreState(store, state);
         
        // console.log(store, state);



         // 每当用户通过mutions修改state中的数据时，就会循环执行_subscribes中的函数
         store._subscribes = [];

        // ---------------------------------------------------------插件实现
        // 执行用户传入的插件函数。
        options.plugins.forEach(plugin => plugin(store));

        
    }
    // 将用户传入的插件函数添加到_subscribes
    subscribe(fn) {
        this._subscribes.push(fn);
    }

    // vux提供的修改state状态的方法，
    replaceState(newState) {
        // 严格模式下 不能直接修改状态
        this._withCommit(() => {
            this._state.data = newState;
        });
    }
    
    // 调用mutions
    commit = (type, payload) => {
        const entry = this._mutations[type] || [];
        // _withCommit：严格模式13
        this._withCommit(() => {
            entry.forEach(handler => handler(payload))
        });
        // subscribe APi：在每个 mutation 完成后调用
        this._subscribes.forEach(sub => sub({ type, payload }, this.state))
    }

    // 调用actions
    dispatch = (type, payload) => {
        // this._actions[type]：存储的函数已经将参数传入了
        const entry = this._actions[type] || []
        return Promise.all(entry.map(handler => handler(payload)))
    }

    // use调用install
    install(app, injectKey) { // createApp().use(store,'my')
        // 全局暴露一个变量 暴露的是store的实例
        app.provide(injectKey || storeKey, this); // 给根app增加一个_provides ,子组件会去向上查找
        // Vue.prototype.$store = this
        app.config.globalProperties.$store = this; // 增添$store属性
    }

     // 用户访问state使用，劫持get()
    get state() {
        return this._state.data
    }

    // 动态添加模块
    registerModule(path, rawModule) {
        // debugger
        const store = this;
        // 用户可能传入的是一个字符串，如果是字符串就会添加到根模块下，这时我们也要包装成数组，统一处理
        if (typeof path == 'string') path = [path];

        // register：根据path数组，递归构建父子关系模块，
        const newModule = store._modules.register(rawModule, path); 
        
        // 在把模块的状态收sh集，收集state，getters、mutions、actions
        // newModule：要将构建好的最新的当前模块传入
        installModule(store, store.state, path, newModule)

        // 重置容器  实现state响应式、收集getters、判断是否开启严格模式
        resetStoreState(store, store.state);
    }
}

// 格式化用户的参数，实现根据自己的需要，后续使用时方便

// root = {
//     _raw:rootModule,
//     state:rootModule.state, // 用户管理 
//     _children:{
//         aCount:{ // > 1
//             _raw:aModule,
//             state:aModule.state,
//             _children:{ // > 1
//                 cCount:{
//                     _raw:useCssModule,
//                     state:cModule.state,
//                     _children:{}
//                 }
//             }
//         },
//         bCount:{
//             _raw:bModule,
//             state:bModule.state,
//             _children:{}
//         }
//     }
// }